import 'dart:async';

import 'package:flutter/material.dart';
import 'package:loadmore/KProgress.dart';
import 'package:meta/meta.dart';
import 'dart:math' as math;

enum _RefreshIndicatorMode {
  drag, // Pointer is down.
  armed, // Dragged far enough that an up event will run the onRefresh callback.
  snap, // Animating to the indicator's final "displacement".
  refresh, // Running the refresh callback.
  done, // Animating the indicator's fade-out after refreshing.
  canceled, // Animating the indicator's fade-out after not arming.
}

const double _kDragContainerExtentPercentage = 0.25;
const double _kDragSizeFactorLimit = 1.5;

/// main code from [RefreshIndicator]
class LoadMore extends StatefulWidget {
  final Widget child;

  final RefreshCallback onRefresh;

  const LoadMore({
    Key key,
    @required this.child,
    @required this.onRefresh,
    this.notificationPredicate = defaultScrollNotificationPredicate,
  }) : super(key: key);

  @override
  _LoadMoreState createState() => new _LoadMoreState();

  final ScrollNotificationPredicate notificationPredicate;
}

class _LoadMoreState extends State<LoadMore> {
  _RefreshIndicatorMode _mode;
  double _dragOffset;
  bool _isIndicatorAtTop;
  Future<Null> _pendingRefreshFuture;

  @override
  Widget build(BuildContext context) {
    Widget child = new NotificationListener<ScrollNotification>(
      child: new NotificationListener<OverscrollIndicatorNotification>(
        child: widget.child,
        onNotification: _handleGlowNotification,
      ),
      onNotification: _handleScrollNotification,
    );

    String text = "";
    print("_mode = $_mode");
    var loading = false;
    switch (_mode) {
      case _RefreshIndicatorMode.armed:
        text = "松手刷新";
        break;
      case _RefreshIndicatorMode.canceled:
        text = "松手刷新";
        break;
      case _RefreshIndicatorMode.done:
        text = "松手刷新";
        break;
      case _RefreshIndicatorMode.drag:
        text = "松手刷新";
        break;
      case _RefreshIndicatorMode.refresh:
        text = "松手刷新";
        loading = true;
        break;
      case _RefreshIndicatorMode.snap:
        text = "松手刷新";
        break;
      default:
        text = "";
    }

    return new Stack(
      children: <Widget>[
        child,
        new Positioned(
            child: new KProgressWidget(
          isLoading: loading,
          text: text,
        )),
      ],
    );
  }

  bool _handleScrollNotification(ScrollNotification notification) {
    if (!widget.notificationPredicate(notification)) return false;
    if (notification is ScrollStartNotification &&
        notification.metrics.extentAfter == 0.0 &&
        _mode == null &&
        _start(notification.metrics.axisDirection)) {
      setState(() {
        _mode = _RefreshIndicatorMode.drag;
      });
      return false;
    }
    bool indicatorAtTopNow;
    switch (notification.metrics.axisDirection) {
      case AxisDirection.down:
        indicatorAtTopNow = false;
        break;
      case AxisDirection.up:
        indicatorAtTopNow = true;
        break;
      case AxisDirection.left:
      case AxisDirection.right:
        indicatorAtTopNow = null;
        break;
    }
    if (indicatorAtTopNow != _isIndicatorAtTop) {
      if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) _dismiss(_RefreshIndicatorMode.canceled);
    } else if (notification is ScrollUpdateNotification) {
      if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) {
        if (notification.metrics.extentAfter > 0.0) {
          _dismiss(_RefreshIndicatorMode.canceled);
        } else {
          _dragOffset -= notification.scrollDelta;
          _checkDragOffset(notification.metrics.viewportDimension);
        }
      }
    } else if (notification is OverscrollNotification) {
      if (_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed) {
        _dragOffset -= notification.overscroll / 2.0;
        _checkDragOffset(notification.metrics.viewportDimension);
      }
    } else if (notification is ScrollEndNotification) {
      switch (_mode) {
        case _RefreshIndicatorMode.armed:
          _show();
          break;
        case _RefreshIndicatorMode.drag:
          _dismiss(_RefreshIndicatorMode.canceled);
          break;
        default:
          // do nothing
          break;
      }
    }
    return false;
  }

  void _checkDragOffset(double containerExtent) {
    assert(_mode == _RefreshIndicatorMode.drag || _mode == _RefreshIndicatorMode.armed);
    double newValue = _dragOffset / (containerExtent * _kDragContainerExtentPercentage);
    if (_mode == _RefreshIndicatorMode.armed) newValue = math.max(newValue, 1.0 / _kDragSizeFactorLimit);
//    _positionController.value = newValue.clamp(0.0, 1.0); // this triggers various rebuilds
    if (_mode == _RefreshIndicatorMode.drag) _mode = _RefreshIndicatorMode.armed;
  }

  bool _handleGlowNotification(OverscrollIndicatorNotification notification) {
    if (notification.depth != 0 || notification.leading) return false;
    if (_mode == _RefreshIndicatorMode.drag) {
      notification.disallowGlow();
      return true;
    }
    return false;
  }

  void _show() {
    assert(_mode != _RefreshIndicatorMode.refresh);
    assert(_mode != _RefreshIndicatorMode.snap);
    final Completer<Null> completer = new Completer<Null>();
    _pendingRefreshFuture = completer.future;
    _mode = _RefreshIndicatorMode.snap;
    if (mounted && _mode == _RefreshIndicatorMode.snap) {
      assert(widget.onRefresh != null);
      setState(() {
        // Show the indeterminate progress indicator.
        _mode = _RefreshIndicatorMode.refresh;
      });

      final Future<Null> refreshResult = widget.onRefresh();
      assert(() {
        if (refreshResult == null)
          FlutterError.reportError(new FlutterErrorDetails(
            exception: new FlutterError('The onRefresh callback returned null.\n'
                'The RefreshIndicator onRefresh callback must return a Future.'),
            context: 'when calling onRefresh',
            library: 'material library',
          ));
        return true;
      }());
      if (refreshResult == null) return;
      refreshResult.whenComplete(() {
        if (mounted && _mode == _RefreshIndicatorMode.refresh) {
          completer.complete();
          _dismiss(_RefreshIndicatorMode.done);
        }
      });
    }
  }

  Future<Null> _dismiss(_RefreshIndicatorMode newMode) async {
    assert(newMode == _RefreshIndicatorMode.canceled || newMode == _RefreshIndicatorMode.done);
    setState(() {
      _mode = newMode;
    });
//    switch (_mode) {
//      case _RefreshIndicatorMode.done:
//        await _scaleController.animateTo(1.0, duration: _kIndicatorScaleDuration);
//        break;
//      case _RefreshIndicatorMode.canceled:
//        await _positionController.animateTo(0.0, duration: _kIndicatorScaleDuration);
//        break;
//      default:
//        assert(false);
//    }
    if (mounted && _mode == newMode) {
      _dragOffset = null;
      _isIndicatorAtTop = null;
      setState(() {
        _mode = null;
      });
    }
  }

  bool _start(AxisDirection direction) {
    assert(_mode == null);
    assert(_isIndicatorAtTop == null);
    assert(_dragOffset == null);
    switch (direction) {
      case AxisDirection.down:
        _isIndicatorAtTop = false;
        break;
      case AxisDirection.up:
        _isIndicatorAtTop = true;
        break;
      case AxisDirection.left:
      case AxisDirection.right:
        _isIndicatorAtTop = null;
        // we do not support horizontal scroll views.
        return false;
    }
    _dragOffset = 0.0;
    return true;
  }

  Future<Null> show({bool atTop: true}) {
    if (_mode != _RefreshIndicatorMode.refresh && _mode != _RefreshIndicatorMode.snap) {
      if (_mode == null) _start(atTop ? AxisDirection.down : AxisDirection.up);
      _show();
    }
    return _pendingRefreshFuture;
  }
}
